package android.matrix;

import java.io.IOException;

/**
 * La clase <code>Matrix</code> es una subclase de <code>AbstracMatrix</code> e implementa la interfase
 * <code>MatrixOps</code> que agrega métodos de operaciones básicas con matrices como multiplicación 
 * escalar, suma, resta, transposición y multiplicación de matrices. También hay funciones de creación 
 * de matrices específicas como la matriz de ceros, matriz de unos y matriz identidad. 
 * 
 * @author Rafael Asprilla
 *
 */
public class Matrix extends AbstractMatrix implements MatrixOps{
	
	/**
	 * Constructor de la clase Matrix.
	 * 
	 * @param e
	 * Arreglo float que contiene el valor de todos los elementos de la matriz.
	 */
	public Matrix(float[][] e)
	{
		super(e);
	}
	
	/** 
	 * El método estático O(int, int) permite crear una matriz con todo su contenido igual a 0's. Esta es una matriz trivial y
	 * permite el reservar espacio en la memoria para una matriz del tamaño determinado o para otras operaciones como suma y resta. 
	 * 
	 * @param rows
	 * Número de filas de la matriz O.
	 * @param columns
	 * Número de columnas de la matriz O.
	 * @return
	 * Retorna una variable tipo Matrix con la matriz O(rows, columns).
	 * @throws IOException 
	 */
	public static Matrix O(int rows, int columns)
	{
		float element[][] = new float[rows][columns];
		for(int i = 0; i < rows; i++)
			for(int j = 0; j < columns; j++)
				element[i][j] = (float) 0; 
		Matrix x = new Matrix(element);
		return x;
	}
	
	/** 
	 * El método estático U(int, int) permite crear una matriz con todo su contenido igual a 1's. Esta es una matriz trivial y
	 * permite el reservar espacio en la memoria para una matriz del tamaño determinado o para otras operaciones como escalar y
	 * multiplicar, además de suma y resta. 
	 * 
	 * @param rows
	 * Número de filas de la matriz U.
	 * @param columns
	 * Número de columnas de la matriz U.
	 * @return
	 * Retorna una variable tipo Matrix con la matriz U(rows, columns).
	 * @throws IOException 
	 */
	public static Matrix U(int rows, int columns)
	{
		float element[][] = new float[rows][columns];
		for(int i = 0; i < rows; i++)
			for(int j = 0; j < columns; j++)
				element[i][j] = (float) 1; 
		Matrix x = new Matrix(element);
		return x;
	}
	
	/**
	 * El método I(int) genera una matriz identidad cuadrada, la cual es una matriz trivial que se utiliza en diferentes operaciones
	 * con matrices.
	 * 
	 * @param rows
	 * Número de filas y columnas de la matriz
	 * @return
	 * Retorna una variable de tipo Matrix con todos los elementos cero excepto en la diagonal principal.
	 * @throws IOException 
	 */
	public static Matrix I(int rows)
	{
		float element[][] = new float[rows][rows];
		for(int i = 0; i < rows; i++)
			for(int j = 0; j < rows; j++)
				if(i == j)
					element[i][j] = (float) 1; 
				else
					element[i][j] = (float) 0;
		Matrix x = new Matrix(element);
		return x;
	}
		
	/* (non-Javadoc)
	 * @see android.matrix.MatrixOps#transpose()
	 */
	@Override
	public Matrix transpose()
	{
		float e[][] = new float[this.columns][this.rows];
		int i, j;
		
		for(j = 0; j < this.columns; j++)
			for(i = 0; i < this.rows; i++)
				e[j][i] = element[i][j];
		
		return new Matrix(e);		
	}
	
	/* (non-Javadoc)
	 * @see android.matrix.MatrixOps#mult(android.matrix.AbstractMatrix)
	 */
	@Override
	public Matrix mult(AbstractMatrix b)
	{
		
		float result[][] = new float[this.rows][b.columns];
		int i, j;
		
		if(this.columns != b.rows)
			return null;
		else
		{
			for(i = 0; i < this.rows; i++)
				for(j = 0; j < b.columns; j++)
				{
					result[i][j] = 0;
					for(int k = 0; k < this.columns; k++)
						result[i][j] += this.element[i][k] * b.getRow(k)[j];
				}
					
			return new Matrix(result);
		}
	}
	
	/* (non-Javadoc)
	 * @see android.matrix.MatrixOps#sum(android.matrix.AbstractMatrix)
	 */
	@Override
	public Matrix sum(AbstractMatrix b)
	{
		float result[][] = new float[this.rows][this.columns];
		int i, j;
		
		if((b.columns != this.columns) || (b.rows != this.rows))
			return null;
		else
		{
			for(i = 0; i < this.rows; i++)
				for(j = 0; j < this.columns; j++)
					result[i][j] = this.element[i][j] + b.getRow(i)[j];
			
			
			return new Matrix(result);
		}
		
	}
	
	/* (non-Javadoc)
	 * @see android.matrix.MatrixOps#sub(android.matrix.AbstractMatrix)
	 */
	@Override
	public Matrix sub(AbstractMatrix b)
	{
		float result[][] = new float[this.rows][this.columns];
		int i, j;
		
		if((b.columns != this.columns) || (b.rows != this.rows))
			return null;
		else
		{
			for(i = 0; i < this.rows; i++)
				for(j = 0; j < this.columns; j++)
					result[i][j] = this.element[i][j] - b.getRow(i)[j];
			return new Matrix(result);
		}
		
	}
	
	/* (non-Javadoc)
	 * @see android.matrix.MatrixOps#scalar(float)
	 */
	@Override
	public Matrix scalar(float c)
	{
		int i, j;
		float x[][] = new float[rows][columns];
		
		for(i = 0; i < rows; i++)
			for(j = 0; j < columns; j++)
				x[i][j] = c * element[i][j];
		
		return(new Matrix(x));
	}
}

